﻿using GameStoreMinimalApi.WebApi.Authorization;
using GameStoreMinimalApi.WebApi.Dtos;
using GameStoreMinimalApi.WebApi.Entities;
using GameStoreMinimalApi.WebApi.Repositories;
using Microsoft.AspNetCore.Http.HttpResults;

namespace GameStoreMinimalApi.WebApi.Endpoints;

public static class GamesEndpoints
{
    const string GetGameV1EndpointName = "GetGameV1";
    const string GetGameV2EndpointName = "GetGameV2";

    public static RouteGroupBuilder MapGamesEndpoints(this IEndpointRouteBuilder routes)
    {
        var group = routes.NewVersionedApi()
                          .MapGroup("/games")
                          .HasApiVersion(1.0)
                          .HasApiVersion(2.0)
                          .WithParameterValidation()
                          .WithOpenApi()
                          .WithTags("Games");

        group.MapGet("", async (
            IGamesRepository repository,
            ILoggerFactory loggerFactory,
            [AsParameters] GetGamesRequestV1 request,
            HttpContext http) =>
        {
            var totalCount = await repository.CountAsync(request.Filter);
            http.Response.AddPaginationHeader(totalCount, request.PageSize);

            return Results.Ok((await repository.GetAllAsync(request.PageNumber, request.PageSize, request.Filter))
                                               .Select(game => game.AsGameResponseV1()));
        })
        .MapToApiVersion(1.0)
        .WithSummary("Gets all games")
        .WithDescription("Gets all available games and allows filtering and pagination");

        group.MapGet("/{id}", async Task<Results<Ok<GameResponseV1>, NotFound>> (IGamesRepository repository, int id) =>
        {
            Game? game = await repository.GetAsync(id);

            if (game is null)
            {
                return TypedResults.NotFound();
            }

            return TypedResults.Ok(game.AsGameResponseV1());
        })
        .WithName(GetGameV1EndpointName)
        .RequireAuthorization(Policies.ReadAccess)
        .MapToApiVersion(1.0)
        .WithSummary("Gets a game by id")
        .WithDescription("Gets teh game that has the specified id");

        group.MapGet("", async (
            IGamesRepository repository,
            ILoggerFactory loggerFactory,
            [AsParameters] GetGamesRequestV2 request,
            HttpContext http) =>
        {
            var totalCount = await repository.CountAsync(request.Filter);
            http.Response.AddPaginationHeader(totalCount, request.PageSize);

            return Results.Ok((await repository.GetAllAsync(request.PageNumber, request.PageSize, request.Filter))
                                               .Select(game => game.AsGameResponseV2()));
        })
        .MapToApiVersion(2.0)
        .WithSummary("Gets all games")
        .WithDescription("Gets all available games and allows filtering and pagination");

        group.MapGet("/{id}", async Task<Results<Ok<GameResponseV2>, NotFound>> (IGamesRepository repository, int id) =>
        {
            Game? game = await repository.GetAsync(id);

            if (game is null)
            {
                return TypedResults.NotFound();
            }

            return TypedResults.Ok(game.AsGameResponseV2());
        })
        .WithName(GetGameV2EndpointName)
        .RequireAuthorization(Policies.ReadAccess)
        .MapToApiVersion(2.0)
        .WithSummary("Gets a game by id")
        .WithDescription("Gets teh game that has the specified id");

        group.MapPost("", async Task<CreatedAtRoute<GameResponseV1>> (IGamesRepository repository, CreateGameRequest createGameRequest) =>
        {
            var game = new Game
            {
                Name = createGameRequest.Name,
                Genre = createGameRequest.Genre,
                Price = createGameRequest.Price,
                ReleaseDate = createGameRequest.ReleaseDate,
                ImageUri = createGameRequest.ImageUri
            };

            await repository.CreateAsync(game);

            return TypedResults.CreatedAtRoute(game.AsGameResponseV1(), GetGameV1EndpointName, new { id = game.Id });
            // CreatedAtRoute вернет в заголовке путь для получения созданного ресурса.
        })
        //.RequireAuthorization(policy =>
        //{ 
        //    policy.RequireRole("Admin");
        //});
        .RequireAuthorization(Policies.WriteAccess)
        .MapToApiVersion(1.0)
        .WithSummary("Creates a new game")
        .WithDescription("Creates a new game with the specified properties");

        group.MapPut("/{id}", async Task<Results<NotFound, NoContent>> (IGamesRepository repository, int id, UpdateGameRequest updateGameRequest) =>
        {
            Game? existingGame = await repository.GetAsync(id);

            if (existingGame is null)
            {
                return TypedResults.NotFound();
            }

            existingGame.Name = updateGameRequest.Name;
            existingGame.Genre = updateGameRequest.Genre;
            existingGame.Price = updateGameRequest.Price;
            existingGame.ReleaseDate = updateGameRequest.ReleaseDate;
            existingGame.ImageUri = updateGameRequest.ImageUri;

            await repository.UpdateAsync(existingGame);

            return TypedResults.NoContent();
        })
        .RequireAuthorization(Policies.WriteAccess)
        .MapToApiVersion(1.0)
        .WithSummary("Updates a game")
        .WithDescription("Updates all game properties for the game that has the specified id");

        group.MapDelete("/{id}", async (IGamesRepository repository, int id) =>
        {
            Game? game = await repository.GetAsync(id);

            if (game is not null)
            {
                await repository.DeleteAsync(id);
            }

            return TypedResults.NoContent();
        })
        .RequireAuthorization(Policies.WriteAccess)
        .MapToApiVersion(1.0)
        .WithSummary("Deletes a game")
        .WithDescription("Deletes the game that has the specified id");

        return group;
    }
}
